﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Magnum.Reflection;
using System.Runtime.Serialization;


namespace CobaCoba
{
     [Serializable, ProtoBuf.ProtoContract]
    public class CBestOutgoingMessage : BaseMessage
    {

        [ProtoBuf.ProtoMember(1)]
        public string MessageType { get; private set; }
        [ProtoBuf.ProtoMember(2)]
        public string Name { get; private set; }
        [ProtoBuf.ProtoMember(3)]
        public string ExtRef { get; private set; }
        [ProtoBuf.ProtoMember(4)]
        public int Status { get; set; }
        [ProtoBuf.ProtoMember(5)]
        public DateTime? TradeDate { get; private set; }
        [ProtoBuf.ProtoMember(6)]
        public DateTime? SettleDate { get; private set; }
        [ProtoBuf.ProtoMember(7)]
        public ImessageContent MsgContent { get; private set; }
        [ProtoBuf.ProtoMember(8)]
        public string ErrMsg { get; set; }
        [ProtoBuf.ProtoMember(9)]
        public string InstructionType { get; set; }
        [ProtoBuf.ProtoMember(10)]
        public string ClassType { get; private set; }
        [ProtoBuf.ProtoMember(11)]
        public string CoreId { get; private set; }
        [ProtoBuf.ProtoMember(12)]
        public Guid BusCorelationId { get; set; }

        [ProtoBuf.ProtoMember(13)]
        public string SecurityCode { get; set; }

        public void Fill<T>(T mc) where T : ImessageContent
        {
            this.MessageType = "OutgoingMessage";
            this.ExtRef = mc.ExternalReference;
            this.TradeDate = mc.InTradeDate;
            this.SettleDate = mc.InSettlementDate;
            this.MsgContent = mc;
            this.Name = mc.TypeOutgoingClassName ;
            this.InstructionType = mc.InstructionType;
            this.ClassType = mc.TypeOutgoingClassName;
            this.CoreId = mc.CoreId;
            this.SecurityCode = mc.SecurityCode;
        }

        public void Fill(ImessageContent mc)
        {
            this.MessageType = "OutgoingMessage";
            this.ExtRef = mc.ExternalReference;
            this.TradeDate = mc.InTradeDate;
            this.SettleDate = mc.InSettlementDate;
            this.MsgContent = mc;
            this.Name = mc.TypeOutgoingClassName;
            this.InstructionType = mc.InstructionType;
            this.ClassType = mc.TypeOutgoingClassName;
            this.CoreId = mc.CoreId;
            this.SecurityCode = mc.SecurityCode;
        }


        private static Dictionary<Type, Dictionary<int, FastProperty>> typeCache = new Dictionary<Type, Dictionary<int, FastProperty>>();
        private static Dictionary<Type, Dictionary<int, string>> typeElCache = new Dictionary<Type, Dictionary<int, string>>();

        public string ToXml()
        {
            dynamic mc = new AmazedSaint.Elastic.Lib.ElasticObject("Message");
            mc.name = this.Name;
            mc.type = this.MessageType;
            var record = mc.Record();
            record.name = "data";

            ElementOutgoingAttribute[] el;

            var msgType = this.MsgContent.GetClassType();
            lock (msgType)
            {
                if (!typeCache.ContainsKey(msgType))
                {
                    var propCacheTmp = new Dictionary<int, FastProperty>();
                    typeCache.Add(msgType, propCacheTmp);
                    var elCacheTmpVal = new Dictionary<int, string>();
                    typeElCache.Add(msgType,elCacheTmpVal);
                    foreach (PropertyInfo pi in msgType.GetProperties())
                    {
                        el = (ElementOutgoingAttribute[]) pi.GetCustomAttributes(typeof(ElementOutgoingAttribute), true);

                        if (el != null && el.Length == 1)
                        {                            
                            propCacheTmp.Add(el[0].Sequence, new FastProperty(pi, BindingFlags.GetProperty));
                            elCacheTmpVal.Add(el[0].Sequence, el[0].Element);
                        }
                    }
                }
            }
            var propCache = typeCache[msgType];
            var elCache = typeElCache[msgType];

            var sortedPropCache = (from entry in propCache orderby entry.Key ascending select entry).ToDictionary(pair => pair.Key, pair => pair.Value);

            foreach (int intKey in sortedPropCache.Keys)
            {
                var fastProperty = sortedPropCache[intKey];
                var field = record.Field();
                field.name = elCache[intKey];
                field <<= fastProperty.Get(this.MsgContent) == null ? string.Empty : fastProperty.Get(this.MsgContent);   
            }

            return (mc > AmazedSaint.Elastic.Lib.FormatType.Xml).ToString();
        }
        
    }

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
    public class ElementOutgoingAttribute:Attribute
    {
        public ElementOutgoingAttribute(int sequence, string parameter)
        {
            this.Sequence = sequence;
            this.Element = parameter;
        }

        public string Element { get; set; }
        public int Sequence { get; set; }
    }

      [Serializable, ProtoBuf.ProtoContract, DataContract]
    public class OTC : InstructionData , ICloneable
    {
        public override object Clone()
        {
            return this.MemberwiseClone();
        }
        private DateTime _inSettlementDate;
        private DateTime _inTradeDate;

        private decimal _inSecuritiesQuantity;
        private decimal _inSettlementAmount;

        public override Type GetClassType()
        {
            return this.GetType();
        }
       

        [ProtoBuf.ProtoMember(1)]
        [ElementOutgoing(1, "externalReference")]
        [DataMember]
        public override string ExternalReference
        {
            get { return base.ExternalReference; }
            set { base.ExternalReference = value; }
        }

        [ProtoBuf.ProtoMember(2)]
        [ElementOutgoing(2, "instructionType")]
        [DataMember]
        public override string InstructionType 
        {
            get { return base.InstructionType;}
            set { base.InstructionType = value;} 
        }

        [ProtoBuf.ProtoMember(3)]
        [ElementOutgoing(3, "participantCode")]
        [DataMember]
        public override string Participant
        {
            get { return base.Participant; }
            set { base.Participant = value; }
        }

        [ProtoBuf.ProtoMember(4)]
        [ElementOutgoing(4, "participantAccount")]
        [DataMember]
        public override string AccountParticipant
        {
            get { return base.AccountParticipant; }
            set { base.AccountParticipant = value; }
        }

        [ProtoBuf.ProtoMember(5)]
        [ElementOutgoing(5, "counterpartCode")]
        [DataMember]
        public override string CounterParty 
        {
            get { return base.CounterParty; }
            set { base.CounterParty = value; } 
        }

        [ProtoBuf.ProtoMember(6)]
        [ElementOutgoing(6, "securityCodeType")]
        [DataMember]
        public override string SecurityCodeType
        {
            get { return base.SecurityCodeType; }
            set { base.SecurityCodeType = value; }
        }

        [ProtoBuf.ProtoMember(7)]
        [ElementOutgoing(7, "securityCode")]
        [DataMember]
        public override string SecurityCode
        {
            get
            {
                return base.SecurityCode;
            }
            set
            {
                base.SecurityCode = value;
            }
        }

        [ProtoBuf.ProtoMember(8)]
        [DataMember]
        public override decimal InSecurityQuantity
        {
            get { return _inSecuritiesQuantity; }
            set { base.InSecurityQuantity = _inSecuritiesQuantity = value; }
        }

        [ProtoBuf.ProtoIgnore]
        [ElementOutgoing(8, "numberOfSecurities")]
        public override string SecurityQuantity
        {
            get
            {
                return string.Format("{0:0.####}", _inSecuritiesQuantity);
            }
            set
            {
                _inSecuritiesQuantity = string.IsNullOrEmpty(value) ? 0 :
                    decimal.Parse(value, System.Globalization.NumberStyles.AllowDecimalPoint,
                    System.Globalization.CultureInfo.CreateSpecificCulture("en-EN"));
            }
        }

        [ProtoBuf.ProtoMember(9)]
        [DataMember]
        public override DateTime InTradeDate
        {
            get { return _inTradeDate; }
            set { base.InTradeDate = _inTradeDate = value; }
        }

        [ProtoBuf.ProtoIgnore]
        [ElementOutgoing(9, "tradeDate")]
        public override string TradeDate
        {
            get
            {
                return ((DateTime)_inTradeDate).ToString("yyyyMMdd");
            }
            set
            {
                _inTradeDate = CommonUtil.ConvertStringToDateTime(value);
            }
        }

        [ProtoBuf.ProtoMember(10)]
        [ElementOutgoing(10, "currencyCode")]
        [DataMember]
        public override string Currency 
        {
            get { return base.Currency; }
            set { base.Currency = value; } 
        }

        [ProtoBuf.ProtoMember(11)]
        [DataMember]
        public override decimal InSettlementAmount
        {
            get { return _inSettlementAmount; }
            set { base.InSettlementAmount = _inSettlementAmount = value; }
        }

        [ProtoBuf.ProtoIgnore]
        [ElementOutgoing(11, "settlementAmount")]
        public override string SettlementAmount
        {
            get
            {
                return string.Format("{0:0.##}", _inSettlementAmount);
            }
            set
            {
                _inSettlementAmount = string.IsNullOrEmpty(value) ? 0 :
                    decimal.Parse(value, System.Globalization.NumberStyles.AllowDecimalPoint,
                    System.Globalization.CultureInfo.CreateSpecificCulture("en-EN"));
            }
        }

        [ProtoBuf.ProtoMember(12)]
        [DataMember]
        public override DateTime InSettlementDate
        {
            get { return _inSettlementDate; }
            set { base.InSettlementDate = _inSettlementDate = value; }
        }

        [ProtoBuf.ProtoIgnore]
        [ElementOutgoing(12, "settlementDate")]
        public override string SettlementDate
        {
            get
            {
                return ((DateTime)_inSettlementDate).ToString("yyyyMMdd");
            }
            set
            {
                _inSettlementDate = CommonUtil.ConvertStringToDateTime(value);
            }
        }


        [ProtoBuf.ProtoMember(13)]
        [ElementOutgoing(13, "description")]
        [DataMember]
        public override string Description
        {
            get { return base.Description; }
            set { base.Description = value; }
        }

        [ProtoBuf.ProtoMember(14)]
        [DataMember]
        public override string Status 
        {
            get { return base.Status; }
            set { base.Status = value; } 
        }


        [ProtoBuf.ProtoIgnore]
        public override string TypeIncomingClassName
        {
            get { return "InstructionData"; }
        }

        [ProtoBuf.ProtoIgnore]
        public override string TypeOutgoingClassName
        {
            get { return "OTC"; }
        }


        [ProtoBuf.ProtoIgnore]
        public override string Extension
        {
            get { return "OTC"; }
        }

        [ProtoBuf.ProtoMember(15)]
        [DataMember]
        public override string CoreId { get; set; }

        [ProtoBuf.ProtoMember(16)]
        [DataMember]
        public string SalePurchaseFlag { get; set; }

        [ProtoBuf.ProtoMember(17)]
        [DataMember]
        public string CustomerName
        {
            get;
            set;
        }

    }
}

